<?
//	********************************************
//	SbSE common functions v0.9.128 / 2012.04.27
//	********************************************
// Changes:
// + html2text, text2search
// * fixed component sorting logic
//	* fixed autover
// + component_filter - context dependent component filtering
// + flattenContext - create plain array of context variables
// + attach youtube video by link
// * attach local video by link fixed
// + memcached processing & invalidating by TOC
// + prepareModel
// + component_fetch caching
// + memcached
// * component filtering logic update: do filtering by tags only if there are no options at all or if tag is explisitly set
// * diags can pass data through itself now
// + database access is adopted to table prefixes usage
// * date parsing extended by time
//	+ addCrumb
// * localized date formatting - date2str
// * component filtering fixed
// * fixed addScriptLink check for file extension to exclude false positives in the middle of the filename
//	* stack() now returns full current stack level data
// + component filtering by tags, passing data to components by tags
// * some image processing fixes
// + component data extractor
// + takeimage file naming logic fixed
// + added scale modes to the takeImage functions - now non-proportional full filled tn can be extracted
// + updated for the site root detection to make use of the cron scripts
// * takeimage: GIF resampling enabled, no animated GIFs accepted now
// * make search_safe unicode aware
// * .jpeg extension support
//	* some db requests fixed due to oracle
// + addHead
// + auto versioning for scripts
// * crumbs takes into attention the PK_REDIRECT pages
// + addScriptLink, addScriptTag
// * startScript can now collect an array or comma delimited string of scripts it depends on
// * component fixes
// * translation functions are exclusively $DATA['t'] based now
// * crumb now contains keywords
// * component_route now works recursively
// * utf82cp1251 ukrainian letters fix
// + format_db_date
// * parse_today can return a date bits now
// * addScript fix
// * permitted
// * str_cut_long
// + cp12512utf8
// + addTranslation, addCpnTranslation
// + t() for component - tc, htc
// + addScript
// + component_localize
// * parse_fname fixes
// + ht() - special characters escape of t()
// * new security logic and fixes
// * paging fixes
// * strip deprecated functions 'ereg' and 'split' from the code for the PHP 5.3 compatibility
// * array_mark_bouds now marks with string values 'first' and 'last'
//	* h() checks for valid utf8 now
// + validate_utf8: check for invalid utf8 to prevent XSS in IE6
//	* updated translation logic: t() can take module paths and placeholders now
// * component processing fixes
// + startScript / endScript - javascript collect functions
// * component processing fixes
// * checkImage: adds a slash between path and name if path doesn't ends with the one
// * killdir can kill dirs recursively now
// + component_exists
// + str_cut_long
// * h() now translates the single quote _'_ too
// + component_route + routing logic
// + array_extend
// + components have COM_ state constants and can be global now
// * takeImage now uses the original filename if only image path is given
// + components fixes
// * paging fixes
// * debug output is for admin only
// + tpl - load template file with params
// * attach flv media via SWFobject + FlowPlayer
// * flv operations are now cached
// + cache & setCache added (D_CACHE in const.php required)
// + component stub
// * array_mark_bounds returns argument unchanged if not an array
// * component management fixes
// + component management
// + createdir
// * takeImage FLV & SWF processing fixes
// + killMedia
// + xmeta
// * attachImage uses xViewer by default. 2do: make template for this
// * array_mark_bounds clears previous bounds now
// + createPaging
// + array_trim_empty
// * crumb pagenames fetch optimization
// + stack processing
// * inCrumb fixes
// * bit processing fixes, $STACK added
// + utf82cp1251
// + postRequest
// + killImage
// + flv support
// + talkImageSize
// + php2js - JSON like transform
// + checkImage
// + copydir, killdir
// + bits processing
// + h(), hdat() - html-encoding quotes
// + takeImage function added, * image processing fixes
// * date functions has been updated
// * image upload function fix - can take an array as $field parameter now
// * image resample functions dimensions calculation
// + unlinkAll($fn) added
// * removed thousands sep from money formatting

$LEVEL = '';
$DIAGS = array();
$MEDIA_TYPES = array(1 => 'gif', 2 => 'jpg', 3 => 'png', 4 => 'swf', 13 => 'swf', 20 => 'flv' );

define('MI_FIT',			1);	//	default, proportional scale
define('MI_CUT_CENTER',	2);	//	fit by lowest source size then cut by the cropbox


if (defined('SUPPORT_FLV')) {
	if (SUPPORT_FLV) { require_once E.'_lib/_flv.php'; }
} else {
	define('SUPPORT_FLV',	false);
}

if (!defined('FLV_RESAMPLE_VIDEO')) {
	define('FLV_RESAMPLE_VIDEO', false);
}

if (!defined('COM_ACTIVE')) define('COM_ACTIVE', COM_ENABLED | COM_VISIBLE);

define('MC_SERVER', '127.0.0.1');
define('MC_PORT', 11211);
define('MC_EXPIRE', 15*60);

try {
	if (defined('USE_MEMCACHED') && USE_MEMCACHED && extension_loaded('Memcached') && class_exists('Memcached')) {
		if (!($GLOBALS['mc'] = new Memcached()))
			throw new Exception('No memcached object');
			
		$GLOBALS['mc']->addServer(MC_SERVER, MC_PORT);
		if ($GLOBALS['mc']->getResultCode())	
			throw new Exception('No memcached server');
			
		define('MC', true);
	} else
		throw new Exception('No memcached extension');
		
} catch(Exception $e) {
	define('MC', false);
}

//debug(MC ? 'mc' : '!mc', 'mc', 1);	
//debug($GLOBALS['mc']->getResultCode() );	

function debug($var, $name='debug', $force = false) {	// req: PHP5
	if ((!readyPermissions()) || permitted('see-debug-output') || $force) {
		print("<pre>\n".$name.": ");
		if (is_scalar($var)) print($var); else print_r($var);
		print("\n</pre>");
		flush();
	}
	return $var;
}

$DBDebug = 0;
function dbdebug($d = 1, $force = false) {	
	if (permitted('see-debug-output') || $force)
		if (defined('USE_ADODB') && isset($GLOBALS['db']))
			$GLOBALS['db']->debug = $d > 0;
		else
			$GLOBALS['DBDebug'] = $d; 
}

function diags($d, $res = false) { $GLOBALS['DIAGS'][] = $d; return $res; }

// ***** js collect processing *****

function autoVer($url){
	$path = pathinfo($url);
	if (substr($path['dirname'], -1, 1) != '/') $path['dirname'] .= '/';
	$ver = ($t = @filemtime($_SERVER['DOCUMENT_ROOT'].$url)) ? $t : '0';
	return "$path[dirname]$path[filename].$ver.$path[extension]";
}

function startScript($s = false) {
	if ($s) {	// collect dependent scripts
		if (is_array($s)) {
			foreach($s as $v)
				if (is_string($v))
					addScriptLink($v);
		} else
		if (is_string($s)) {
			$s = array_trim_empty_str(explode(',', $s));
			foreach($s as $v)
				if (is_string($v))
					addScriptLink($v);
		}
	}
	ob_start(); // start inline script collecting
}
function endScript() { $GLOBALS['SCRIPT']['inline'][] = ob_get_clean(); }

function addScriptLink($s = false) {
	if ($s) {
		$t = explode(',', $s);
		foreach($t as $s) {
			$s = trim($s);
			
		if (($s{0} != '/') && ($local = (strpos($s, '://') === false))) $s = "/lib/$s";
			if ((strripos($s, '.js') !== strlen($s) - 3) && (strripos($s, '.js.php') !== strlen($s) - 7)) $s .= '.js';
		if ($local)
			$s = autoVer($s);
		if (!in_array($s, (array)$GLOBALS['SCRIPT']['link'])) $GLOBALS['SCRIPT']['link'][] = $s; 
	}
}
}

function addScript($s = false) {
	if ($s) {
		if ($s{0} != '/') $s = "/lib/$s";
		if (stripos($s, '.js') === false) $s .= '.js';
		if (!in_array($t = "<script type=\"text/javascript\" src=\"$s\"></script>", (array)$GLOBALS['SCRIPT']['tag'])) 
			$GLOBALS['SCRIPT']['tag'][] = $t; 
	}
}

function addScriptTag($s = false) {
	if ($s) {
		if ($s{0} != '/') $s = "/lib/$s";
		if (stripos($s, '.js') === false) $s .= '.js';
		if (!in_array($t = "<script type=\"text/javascript\" src=\"$s\"></script>", (array)$GLOBALS['SCRIPT']['tag'])) 
			$GLOBALS['SCRIPT']['tag'][] = $t; 
	}
}





// function meta($key, $def='') { return isset($GLOBALS['META'][$key]) ? $GLOBALS['META'][$key] : $def; }
// ***** собираем стили в массив и вывподим в HTML  *****
function Css ($fn='', $p=array()) {
	//global $CFG, $Core;
	global $META,$DATA;
	if (!isset($p['mergename'])) $p['mergename'] = 'main';
	
	//echo '$fn= '.$fn.'<br>';
	$path = rtrim($_SERVER['DOCUMENT_ROOT'],'/').'/';
	//$h = 'http://'.$_SERVER['HTTP_HOST'].'/';
	
	//array_show($META['css'],'$META[css](00)');
	if (!empty($fn)) {
		$fn = '//'.str_replace('.css','',ltrim($fn,'/')).'.css';
		//$META['css'][] = $fn;
		//echo '$fn(css)= `'.$fn.'`<br/>';
		//echo 'file_exists('.$path.$this->data['sess']['skin_path'].'css/'.$fn.'.css)= '.file_exists($path.$this->data['sess']['skin_path'].'css/'.$fn).'<br/>';
		//echo 'file_exists('.$this->Smarty->template_dir.'css/'.$fn.'.css)= '.file_exists($this->Smarty->template_dir.'css/'.$fn).'<br/>';
		//echo 'file_exists('.$this->Smarty->template_dir.$this->data['sess']['skin_name'].'/css/'.$fn.'.css)= '.file_exists($this->Smarty->template_dir.$this->data['sess']['skin_name'].'/css/'.$fn).'<br/>';
		//echo 'file_exists('.$path.$fn.'.css)'.file_exists($path.$fn).'<br/>';
		//echo 'file_exists('.$path.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.$fn.'.css)= '.file_exists($path.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.$fn).'<br/>';
		if (file_exists($path.$fn) AND (!count($META['css'][$p['mergename']]) OR !in_array($path.$fn,$META['css'][$p['mergename']]))) {
			//echo '1-1<br/>';
			$META['css'][$p['mergename']][] = $fn;
		}
		//array_show($META['css'],'$META[css](01)');
	} else {
		if (count($META['css'][$p['mergename']])) sort($META['css'][$p['mergename']]);
		//array_show($META['css'],'$META[css](02)');
		//if (!count($META['css'])) {
		//if ($this->intro AND $test_template=='intro.tpl') {
		//echo 'substr($this->data[points][menu_this][template],0,strrpos('.$test_template.',`.`))= '.substr($test_template,0,strrpos($test_template,'.')).'<br/>';
		//if ($this->intro AND !empty($test_template) AND file_exists($path.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.substr($test_template,0,strrpos($test_template,'.')).'.css')) {
		$META['css'][$p['mergename']] = array_merge((array)'//style/print.css',(array)$META['css'][$p['mergename']]);
		if (!empty($test_template) AND file_exists($path.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.substr($test_template,0,strrpos($test_template,'.')).'.css')) {
			$META['css'][$p['mergename']] = array_merge(array($path.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.substr($test_template,0,strrpos($test_template,'.')).'.css'),(array)$META['css'][$p['mergename']]);
		} elseif ($test_errorPage) {
			$META['css'][$p['mergename']] = array_merge(array($path.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.$test_errorPage.'.css'),(array)$META['css'][$p['mergename']]);
		} else {
			////$META['css'][$p['mergename']][] = '//style/sitewide.css';
			$META['css'][$p['mergename']] = array_merge((array)'//style/sitewide.css',(array)$META['css'][$p['mergename']]);
		}
		//$META['css'][$p['mergename']] = array('//style/sitewide.css','//style/sitewide2.css');
		//array_show($META['css'],'$META[css])');
		$META['css'][$p['mergename']] = array_merge((array)'//style/reset.css',(array)$META['css'][$p['mergename']]); ////$META['css'][$p['mergename']][] = '//style/reset.css';
		if (count($META['css'])) {
			//array_show($META['css'],'$META[css])');
			//$this->Inc->LoadFile('core/lib/_other/minify/lib/Minify/HTML/Helper.php');
			require_once '_lib/_other/minify/lib/Minify/HTML/Helper.php';
			//echo '$_REQUEST[debug]= '.$_REQUEST['debug'].'<br/>';
			foreach ($META['css'] as $val) {
				$r .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"".Minify_HTML_Helper::getUri($val,array( // default options
					'minAppUri'=>'/_lib/_other/minify/',
					'debug'=>($_REQUEST['debug'])?true:false, // $CFG['isAdmin'] OR
					//'farExpires' => true
					//,'debug' => false
					//,'charset' => 'UTF-8'
					//,'minAppUri' => '/min'
					//,'rewriteWorks' => true
					//,'groupsConfigFile' => ''
				))."\"/>\n";
				//echo '$r= '.$r.'<br/>';
			}
		}
		return $r;
	}
	
	
	
	
	
	/*
	if (!isset($p['merge'])) $p['merge'] = true;
	if (!isset($p['mergename'])) $p['mergename'] = 'main';
	if (!empty($fn)) {
		$fn = str_replace('.css','',$fn);
		//echo '$fn(css)= `'.$fn.'`<br/>';
		//echo 'file_exists('.$CFG['path'].$this->data['sess']['skin_path'].'css/'.$fn.'.css)= '.file_exists($CFG['path'].$this->data['sess']['skin_path'].'css/'.$fn).'<br/>';
		//echo 'file_exists('.$this->Smarty->template_dir.'css/'.$fn.'.css)= '.file_exists($this->Smarty->template_dir.'css/'.$fn).'<br/>';
		//echo 'file_exists('.$this->Smarty->template_dir.$this->data['sess']['skin_name'].'/css/'.$fn.'.css)= '.file_exists($this->Smarty->template_dir.$this->data['sess']['skin_name'].'/css/'.$fn).'<br/>';
		//echo 'file_exists('.$CFG['path'].$fn.'.css)'.file_exists($CFG['path'].$fn).'<br/>';
		//echo 'file_exists('.$CFG['path'].($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.$fn.'.css)= '.file_exists($CFG['path'].($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.$fn).'<br/>';
		if (file_exists($CFG['path'].$fn)) {
			//echo '1-1<br/>';
			if (!count($META['css']) OR !in_array('//'.$fn,$META['css'])) $META['css'][] = '//'.$fn;
		} elseif (file_exists($CFG['path'].($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.$fn)) {
			//echo '2-1<br/>';
			if (!count($META['css']) OR !in_array('//'.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.$fn,$META['css'])) $META['css'][] = '//'.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.$fn;
		}
		//array_show($this->cssload,'$this->ssload)');
	} else {
		$this->Inc->LoadFile('core/lib/_other/minify/lib/Minify/HTML/Helper.php');
		//if (!count($this->cssload)) {
		//if ($this->intro AND $test_template=='intro.tpl') {
		//echo 'substr($this->data[points][menu_this][template],0,strrpos('.$test_template.',`.`))= '.substr($test_template,0,strrpos($test_template,'.')).'<br/>';
		//if ($this->intro AND !empty($test_template) AND file_exists($CFG['path'].($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.substr($test_template,0,strrpos($test_template,'.')).'.css')) {
		if (!empty($test_template) AND file_exists($CFG['path'].($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.substr($test_template,0,strrpos($test_template,'.')).'.css')) {
			$META['css'] = array_merge(array('//'.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.substr($test_template,0,strrpos($test_template,'.')).'.css'),(array)$META['css']);
		} elseif ($test_errorPage) {
			$META['css'] = array_merge(array('//'.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/'.$test_errorPage.'.css'),(array)$META['css']);
		} else {
			$META['css'] = array_merge(array('//'.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/index.css'),(array)$META['css']);
		}
		$META['css'] = array_merge(array('//'.($CFG['isAdmin']?'admin/':'').$CFG['themes']['skins'].$this->data['sess']['skin_name'].'/css/master.css'),(array)$META['css']);
		//}
		if (count($this->cssload)) {
			//array_show($this->cssload,'$this->ssload)');
			//echo '$_REQUEST[debug]= '.$_REQUEST['debug'].'<br/>';
			foreach ($this->cssload as $val) {
				$return .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"".Minify_HTML_Helper::getUri($val,array( // default options
						'minAppUri'=>$CFG['host'].'core/lib/_other/minify',
						'debug'=>($_REQUEST['debug'])?true:false, // $CFG['isAdmin'] OR
						//'farExpires' => true
						//,'debug' => false
						//,'charset' => 'UTF-8'
						//,'minAppUri' => '/min'
						//,'rewriteWorks' => true
						//,'groupsConfigFile' => ''
				))."\"/>\n";
			}
			//echo '$return= '.$return.'<br/>';
		}
		return $return;
	}
	*/
}
// ***** собираем стили в массив и вывподим в HTML - END *****


// ***** meta collect processing *****

function addHead($name, $value, $section = 'meta') {
	if ($name && $value)
		$GLOBALS['HEAD'][$section][$name] = $value;
}

// ***** cache functions *****

function mc($key, $def = false) {
	return (MC && $data = $GLOBALS['mc']->get(MC_PREFIX.str_replace(' ', '_', $key))) ? $data : $def;
}

function mcSet($key, $data, $expire) {
	if (MC) {
		$mckey = MC_PREFIX.str_replace(' ', '_', $key);
		$GLOBALS['mc']->set($mckey, $data, $expire); 
		mcAddKeyTOC($mckey);
	}
}

function mcAddKeyTOC($key) {
	if (MC && $key) {
		if (is_array($keys = $GLOBALS['mc']->get(MC_PREFIX.'toc'))) {
			if (isset($keys[$key])) return true;
			$keys[$key] = true;
		} else {
			$keys = array($key => true);
		}
		$GLOBALS['mc']->set(MC_PREFIX.'toc', $keys, MC_EXPIRE); 
	}
}

function mcInvalidate($key) {
	if (MC && $key) {
		$key = MC_PREFIX.$key;
		if (is_array($keys = $GLOBALS['mc']->get(MC_PREFIX.'toc'))) {
			foreach($keys as $k => $v) {
				if (strpos($k, $key) === 0) {
					$GLOBALS['mc']->delete($k);
					unset($keys[$k]);
				}
			}
			$GLOBALS['mc']->set(MC_PREFIX.'toc', $keys, MC_EXPIRE);
		} else {
			$GLOBALS['mc']->flush();
		}
	}
}


function cache($id, $version, $key='') {
	if ($key) {
		if ($t = datafetch('db_item', tb('data'), 'id_data='.D_CACHE." AND assoc='$id' AND tag='$version' AND name='$key'"))
			return $t['value'];
		else
			return false;
	} else
		return datafetch_array('db_list', array('table' => tb('data'), 'where' => 'id_data='.D_CACHE." AND assoc='$id' AND tag='$version'", 'index' => 'name'));
}

function setCache($id, $version, $key, $value='') {
	if (is_array($key)) {

		$temp = array();	foreach (array_keys($key) as $v) $temp[] = "'".sql_safe($v)."'";	$keys = '('.implode(',', $temp).')';
		dataset('db_delete', tb('data'), 'id_data='.D_CACHE." AND assoc='$id' AND name in $keys");
		
		$temp = array();	$_record = array('id_data' => D_CACHE, 'assoc' => $id, 'tag' => $version);
		foreach ($key as $k => $v) $temp[] = array_merge($_record, array('name' => $k, 'value' => $v));
											
		dataset('db_insert', tb('data'), $temp);
											
	} else {
		dataset('db_delete', tb('data'), 'id_data='.D_CACHE." AND assoc='$id' AND name='$key'");
		dataset('db_insert', tb('data'), array('id_data' => D_CACHE, 'assoc' => $id, 'tag' => $version, 'name' => $key, 'value' => $value));
	}
}

// *************** Navigation functions *********************

function getCrumb($path, $hook = 'crumb') {
	global $CURRENT;
	
	$res = array();
	$lang = context('lang_prefix');
	
	foreach ($path as $cr) {
		$res[] = array(
			'id' => $cr['struct']['id'],
			'tag' => $cr['struct']['tag'],
			'name' => $cr['struct']['localname'] ? $cr['struct']['localname'] : $cr['struct']['name'],
			'path' => $CURRENT->getAliasById($cr['struct']['id'], $lang),
			'keyword' => $cr['struct']['keyword'],
		);
	}

	return array_mark_bounds($res);	
}
			
function addCrumb($data) {
	global $DATA;
	if (is_array($data)) {
		if (isset($data['name']))
			$DATA['crumb'][] = $data;
		else
			foreach($data as $v)
				if (is_array($v))
					$DATA['crumb'][] = $v;
	} else if (is_string($data)) {
		$DATA['crumb'][] = array('name' => $data);
				} else 
		return;

	$DATA['crumb'] = array_mark_bounds($DATA['crumb']);
}

function inCrumb($id) {
	if (is_string($id)) {	// assume $id is a path started from root /
		if ($id) {
			foreach($GLOBALS['DATA']['crumb'] as $v) {
				if (strpos($v['path'], $id) === 0) 
					return $v;
			}
		}
			
	} else	// assumes $id is a page id
		foreach($GLOBALS['DATA']['crumb'] as $v)
			if ($v['id'] == $id) return $v;
			
	return false;
}

function createTitle() {
	$res = array();
	foreach($GLOBALS['DATA']['crumb'] as $v)
		if (!($v['tag'] & PT_MAIN))
			if ($v['name'])
				$res[] = $v['name'];
	return $res;
}


function createPaging($p) {
	$def = array(
		'perPage' => 6,
		'pagesBefore' => 2, 'pagesAfter' => 2,
		'pagesAtStart' => 1, 'pagesAtEnd' => 1,
		'titlePrev' => '&larr;', 'titleNext' => '&rarr;',
		'param' => 'page',
		'generate' => 'prev, numbers, next',
		'url' => $GLOBALS['CURRENT']->url,
		'num' => 0, 'page' => 1,
		'data' => array(),
		'limit' => array(0,0)
	);
	
	$p['perPage'] = abs((int)$p['perPage']);
	$p['num'] = abs((int)$p['num']);
	$p['page'] = abs((int)$p['page']);

	$p = array_merge($def, array_trim_empty($p));
	
	if ($p['num'] && $p['perPage']) {
		
		if (($p['lastPage'] = floor(($p['num'] - 1) / $p['perPage']) + 1) > 1) { // no need of paging if 1 page
			
			$p['page'] = ($p['page']  > $p['lastPage']) ? $p['lastPage'] : $p['page'];	// 1 - based page numbering
			
			$what = array_trim_empty_str(explode(',', $p['generate']));
			
			foreach ($what as $w) {
				switch ($w) {
					
					case 'prev':
						if ($before = $p['page'] - 1)
							$p['data'][] = array(
								'title' => $p['titlePrev'], 
								'url' => ($before == 1) ? _transformURL($p['url'], array('remove_param' => $p['param'])) : _transformURL($p['url'], array('add_param' => "$p[param]:$before")));
						break;
	
					case 'next':
						if (($next = $p['page'] + 1) <= $p['lastPage'])
							$p['data'][] = array(
								'title' => $p['titleNext'], 
								'url' => _transformURL($p['url'], array('add_param' => "$p[param]:$next")));
						break;
						
					case 'numbers':
						if ($p['page'] > 1) {
							if ($p['page'] > $p['pagesAtStart'] + $p['pagesBefore'] + 1) {
								for ($i = 1; $i <= $p['pagesAtStart']; $i++)
									$p['data'][] = array(
										'title' => $i, 
										'url' => ($i == 1) ? _transformURL($p['url'], array('remove_param' => $p['param'])) : _transformURL($p['url'], array('add_param' => "$p[param]:$i")));
									
								$p['data'][] = array(
									'title' => '', 
									'url' => '');
									
								for ($i = $p['page'] - $p['pagesBefore']; $i < $p['page']; $i++)
									$p['data'][] = array(
										'title' => $i, 
										'url' => _transformURL($p['url'], array('add_param' => "$p[param]:$i")));
							} else {
								for ($i = 1; $i < $p['page']; $i++)
									$p['data'][] = array(
										'title' => $i, 
										'url' => ($i == 1) ? _transformURL($p['url'], array('remove_param' => $p['param'])) : _transformURL($p['url'], array('add_param' => "$p[param]:$i")));
							}
						}
						
						$p['data'][] = array(
							'title' => $p['page'], 
							'url' => '');

						if ($p['page'] < $p['lastPage']) {
							if ($p['page'] < $p['lastPage'] - $p['pagesAtEnd'] - $p['pagesAfter']) {
								for ($i = $p['page'] + 1; $i <= $p['page'] + $p['pagesAfter']; $i++)
									$p['data'][] = array(
										'title' => $i, 
										'url' => _transformURL($p['url'], array('add_param' => "$p[param]:$i")));
									
								$p['data'][] = array(
									'title' => '', 
									'url' => '');
									
								for ($i = $p['lastPage'] - $p['pagesAtEnd'] + 1; $i <= $p['lastPage']; $i++)
									$p['data'][] = array(
										'title' => $i, 
										'url' => _transformURL($p['url'], array('add_param' => "$p[param]:$i")));
							} else {
								for ($i = $p['page'] + 1; $i <= $p['lastPage']; $i++)
									$p['data'][] = array(
										'title' => $i, 
										'url' => _transformURL($p['url'], array('add_param' => "$p[param]:$i")));
							}
						}

						break;
				}
			}	// foreach
		}
	}
	$p['data'] = array_mark_bounds($p['data']);
	$p['limit'] = array(($p['page'] - 1) * $p['perPage'], $p['perPage']);
	return $p;
}



// *** JSON ***

function php2js($a=false) {
  if (is_null($a)) return 'null';
  if ($a === false) return 'false';
  if ($a === true) return 'true';
  if (is_scalar($a))
  {
    if (is_float($a))
    {
      // Always use "." for floats.
      $a = str_replace(",", ".", strval($a));
    }

    // All scalars are converted to strings to avoid indeterminism.
    // PHP's "1" and 1 are equal for all PHP operators, but
    // JS's "1" and 1 are not. So if we pass "1" or 1 from the PHP backend,
    // we should get the same result in the JS frontend (string).
    // Character replacements for JSON.
    static $jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'),
    array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'));
    return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $a) . '"';
  }
  $isList = true;
  for ($i = 0, reset($a); $i < count($a); $i++, next($a))
  {
    if (key($a) !== $i)
    {
      $isList = false;
      break;
    }
  }
  $result = array();
  if ($isList)
  {
    foreach ($a as $v) $result[] = php2js($v);
    return '[ ' . join(', ', $result) . ' ]';
  }
  else
  {
    foreach ($a as $k => $v) $result[] = php2js($k).': '.php2js($v);
    return '{ ' . join(', ', $result) . ' }';
  }
}


// *************** Translation functions *********************

//		t
// 	$text - string to translate
//		$path - translating module (like plugins/article)
//			$path can be omitted
//		$args - array of "placeholder" => value
//			!placeholder - doesn't modify
//			@placeholder - escapes special html characters
//
//		return value: string of translated text

function t($text, $path = '', $args = array()) {
	global $DATA;

	if ($text) {
		
//		$base = $DATA['translation'];
		$base = $DATA['t'];
		
		if (is_string($path)) {
			if (isset($DATA['t']) && !empty($DATA['t'])) {
				$t = explode('/', $path);
				$base = $DATA['t'];

				foreach($t as $v)
					if (($v = trim($v)) && isset($base[$v]))
						$base = $base[$v];
					else {
//						$base = $DATA['translation'];
						$base = $DATA['t'];
						break;
					}
			} 
				
		} else
			if (is_array($path) && empty($args))
				$args = $path;

		if (isset($base[$text]))
			$text = is_string($base[$text]) ? $base[$text] : $base[$text]['value'];

		if (!empty($args)) {

			foreach ($args as $key => $value) {
				switch ($key[0]) {
					case '@':
						$args[$key] = h($value);	// filter special chars
						break;
				}
			}
			 
			$text = strtr($text, $args);
		}
	}

	return $text;
}

function tc($text, $args = array()) {
	return ($c = stack('name')) ? t($text, "cpn/$c", $args) : t($text, '', $args);
}

function htc($text, $args = array()) {
	return ($c = stack('name')) ? ht($text, "cpn/$c", $args) : ht($text, '', $args);
}

function ht($text, $path = '', $args = array()) {
	global $DATA;

	if ($text) {
		
//		$base = $DATA['translation'];
		$base = $DATA['t'];
		
		if (is_string($path)) {
			if (isset($DATA['t']) && !empty($DATA['t'])) {
				$t = explode('/', $path);
				$base = $DATA['t'];
				
				foreach($t as $v)
					if (($v = trim($v)) && isset($base[$v]))
						$base = $base[$v];
					else {
//						$base = $DATA['translation'];
						$base = $DATA['t'];
						break;
					}
			} 
				
		} else
			if (is_array($path) && empty($args))
				$args = $path;

		if (isset($base[$text]))
			$text = is_string($base[$text]) ? $base[$text] : $base[$text]['value'];

		if (!empty($args))
			$text = strtr($text, $args);
	}

	return h($text);
}

function addTranslation($path = '', $data = array()) {
	global $DATA;
	$base = '$DATA[\'t\']';

	if (is_string($path)) {
		if ($path) {
			$t = explode('/', $path);

			foreach($t as $v) {
				$base = $base."['".trim($v)."']";
				if (!eval("return isset($base) && is_array($base);"))
					eval("$base = array();");
			}
		}
			
	} else
		if (is_array($path) && empty($data))
			$data = $path;

	if (is_array($data) && sizeof($data))
		$temp = array_merge(eval("$base = "), $data);
		eval("$base = array_merge($base, ".'$data);');
}

function addCpnTranslation($data, $unstack = true) {
	if (is_array($data) && sizeof($data) && ($c = stack('name')))
		addTranslation("cpn/$c", $data);
	if ($unstack) unstack();	// restore stack
}


function bl($text) {
	global $DATA;

	if ($text)

		if (isset($DATA["block"][$text])) {
			return $DATA["block"][$text]["value"];
		} else {
			return $text;
		}

	else return $text;
}

// *************** Money functions *********************

function formatPrice($p) {
	if (p)
		if ($p == (int)$p) 
			return number_format($p,0,'','');
		else
			return number_format($p,2,',','');
	else return false;
}

// *************** Date functions *********************

function parse_date($d) {
	$t = false;

	if (preg_match('/^(\d{2,4})-(\d{1,2})-(\d{1,2})$/', $d, $r))
		$t = array('y' => $r[1], 'm' => $r[2], 'd' => $r[3]);
	else 
		if (preg_match('/^(\d{1,2})\.(\d{1,2})\.(\d{2,4})$/', $d, $r))
			$t = array('y' => $r[3], 'm' => $r[2], 'd' => $r[1]);

	$temp = $t ? getdate(mktime(0, 0, 0, $t['m'], $t['d'], $t['y'])) : getdate();

	return array('d' => $temp['mday'], 'm' => $temp['mon'], 'y' => $temp['year'], 'w' => $temp['wday'], 'full' => $temp['year'].'-'.d2d($temp['mon']).'-'.d2d($temp['mday']) );
}

function parse_datetime($d) {	// req: PHP>5.1.0
	list($tm_year, $tm_mon, $tm_day, $tm_hour, $tm_min, $tm_sec) = preg_split("/[-:\s]+/", $d);
		$temp = getdate(mktime((int)$tm_hour, (int)$tm_min, (int)$tm_sec, (int)$tm_mon, (int)$tm_day, (int)$tm_year));
		return array('d' => $temp['mday'], 'm' => $temp['mon'], 'y' => $temp['year'], 'w' => $temp['wday'], 'full' => $temp['year'].'-'.d2d($temp['mon']).'-'.d2d($temp['mday']),
			'rss' => date(DATE_RSS, $temp[0]) );
	return false;
}

function parse_today($what = 'all') {
	$temp = getdate();
	switch ($what) {
		case 'd': return $temp['mday'];
		case 'm': return $temp['mon'];
		case 'y': return $temp['year'];
		case 'w': return $temp['wday'];
		case 'h': return $temp['hours'];
		case 'M': return $temp['minutes'];
		case 's': return $temp['seconds'];
		case 'full': return $temp['year'].'-'.d2d($temp['mon']).'-'.d2d($temp['mday']);
		case 'all':
		default:
			return array(
				'd' => $temp['mday'], 
				'm' => $temp['mon'], 
				'y' => $temp['year'], 
				'w' => $temp['wday'], 
				'h' => $temp['hours'], 
				'M' => $temp['minutes'], 
				's' => $temp['seconds'], 
				'full' => $temp['year'].'-'.d2d($temp['mon']).'-'.d2d($temp['mday']) 
			);
	}
}

function parse_now($timestamp = 0) {
	$temp = $timestamp ? getdate($timestamp) : getdate();
	return $temp['year'].'-'.d2d($temp['mon']).'-'.d2d($temp['mday']).' '.d2d($temp['hours']).':'.d2d($temp['minutes']).':'.d2d($temp['seconds']);
}

function d2d($d) {	// return number in 2 digits
	$d = (int)$d;
	if ($d < 10) return "0".$d; else return $d;
}

function y2d($d) {	// return year in 2 digits
	return d2d(((int)$d) % 100);
}

function y4d($d) {	// return year in 4 digits
	$d = (int)$d;
	return ($d > 99) ? $d : (($d > 69) ? 1900 + $d : 2000 + $d);
}

function date2str($d, $lang = 'en') {	// !!! date in russian. need 2b sync with const.php
	switch($lang) {
		case 'uk':
			$m = array(1 => "січня", "лютого", "березня", "квітня", "травня", "червня", "липня", "серпня", "вересня", "жовтня", "листопада", "грудня");
			return "{$d[d]} {$m[$d[m]]} {$d[y]} р.";
			break;

		case 'ru':
			$m = array(1 => "января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря");
			return "{$d[d]} {$m[$d[m]]} {$d[y]} г.";
			break;
		
		case 'en':
		default:
	return date("D, d M Y", mktime(0,0,0, $d['m'], $d['d'], $d['y']));
	}

}


function format_comment_date($d) {
	return preg_replace('/(\d{2,4})-(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})/i', '$3.$2.$1 $4:$5', $d);
}

function format_db_date($d) {
	return preg_replace('/(\d{1,2}).(\d{1,2}).(\d{2,4})/i', '$3-$2-$1', $d);
}


function format_simple_date($d) {
	if (preg_match('/(\d{2,4})-(\d{1,2})-(\d{1,2})/i', $d, $r)) {
		return d2d($r[3]).'.'.d2d($r[2]).'.'.y4d($r[1]);
	}
}

// *************** File name functions *********************

function parse_fname($fname) {
	if (($slash = strrpos($fname, '/')) === false) {
		$fn = $fname;
		$path = '';
	} else {
		$fn = substr($fname, $slash + 1);
		$path = substr($fname, 0, $slash);
	}
	preg_match('/(.+)\.(\w+)$/', $fn, $regs);
	return array('path' => $path, 'name' => $regs[1], 'ext' => $regs[2]);
}

function fn_numeric_compare ($a, $b) {
	if ($a == $b) return 0;
		else
			if (strnatcasecmp($a,$b) > 0)
				return 1;
			else 
				return -1;
}

function isFileSafe($fn) {
	$DeniedExt = implode('|', array('php','php3','php5','phtml','asp','aspx','ascx','jsp','cfm','cfc','pl','bat','exe','dll','reg','cgi'));
	return !preg_match("/\.[$DeniedExt]$/", $fn);
}

function unlinkAll($fn) {
	foreach(glob($_SERVER[DOCUMENT_ROOT].$fn) as $f)
		unlink($f);
}

function copydir($src,$dest) {
	@mkdir($dest);
	if ($dir = @opendir($src)) {
		while (($file = @readdir($dir))!==false)
			if (is_file("$src/$file"))
				@copy("$src/$file", "$dest/$file");
		closedir($dir);
	}
}

function killdir($src, $recursive = false) {
	if ($dir = @opendir($src)) {
		while (($file = @readdir($dir))!==false)
			if (is_file("$src/$file"))
				@unlink("$src/$file");
			else
				if ($recursive && is_dir("$src/$file")) 
					killdir("$src/$file");
		closedir($dir);
	}
	@rmdir($src);
}

function killImage($path, $fn) {
	if ($t = isImage($path, $fn))
		@unlink("$_SERVER[DOCUMENT_ROOT]$path$t");
}

function killMedia($path, $fn) {
	if ($t = isMedia($path, $fn))
		@unlink("$_SERVER[DOCUMENT_ROOT]$path$t");
}

function createdir($path) {
	$dirs = explode('/', $path = str_replace(array('//', '..'), array('/', '.'), $path));
	$temp = $_SERVER['DOCUMENT_ROOT'];
	
	foreach ($dirs as $p)
		if ($p = trim($p))
			if (!is_dir($temp .= "/$p"))
				mkdir($temp);
	return $path;
}


// *************** Image functions *********************

function talkImageSize($x,$y) {
	if ($x && $y)
		return t("!x x !y px", array('!x' => $x, '!y' => $y));
	else if ($x)
		return t("!x px wide", array('!x' => $x));
	else if ($y)
		return t("!y px height", array('!y' => $y));
	else
		return t("any reasonable");
}

function isImage($path, $fname='') {
	$ext = array('jpg', 'jpeg', 'png', 'gif', 'JPG', 'JPEG', 'PNG', 'GIF');
	
	foreach ($ext as $e) {
		if (is_file("$_SERVER[DOCUMENT_ROOT]$path$fname.$e"))
			return "$fname.$e";
	}
	return false;
}

function isMedia($path, $fname='') {
	$ext = array('jpg', 'jpeg', 'png', 'gif', 'swf', 'flv', 'JPG', 'JPEG', 'PNG', 'GIF', 'SWF', 'FLV');
	
	foreach ($ext as $e) {
		if (is_file("$_SERVER[DOCUMENT_ROOT]$path$fname.$e"))
			return "$fname.$e";
	}
	return false;
}

function checkImage($path, $fname='') {
	$ext = array('jpg' => 1, 'jpeg' => 1, 'png' => 1, 'gif' => 1, 'swf' => 1, 'flv' => 2, 'JPG' => 1, 'JPEG' => 1, 'PNG' => 1, 'GIF' => 1, 'SWF' => 1, 'FLV' => 2);

	if ($fname && (substr($path, -1, 1)) != '/') $path .= '/';

	if (is_file($fn = "$_SERVER[DOCUMENT_ROOT]$path$fname")) {
		$f = parse_fname($path.$fname);

		if (($e = $f['ext']) && ($t = $ext[$e])) {

			switch($t) {
				case 1: // image
					if ($temp = @getimagesize($fn))
						return array('x' => $temp[0], 'y' => $temp[1], 'fn' => "$path$fname", 'type' => strtolower($e));
					break;
					
				case 2: // flv video
					if (SUPPORT_FLV) {
						if (D_CACHE) {
							$ver = filemtime($fn);
							if (($c = cache($fn, $ver)) && $c['x'] && $c['y'])
								return array('x' => $c['x']['value'], 'y' => $c['y']['value'], 'fn' => "$path$fname", 'type' => strtolower($e));
						}
						
						$getID3 = new getID3;
						$temp = $getID3->analyze($fn);
						if ($t1 = $temp['video']) {
							if (D_CACHE) setCache($fn, $ver, array('x' => $t1['resolution_x'], 'y' => $t1['resolution_y']));
							return array('x' => $t1['resolution_x'], 'y' => $t1['resolution_y'], 'fn' => "$path$fname", 'type' => strtolower($e));
						}
					}
					break;
			}

		}
	}
	
	foreach ($ext as $e => $t)
		if (is_file($fn = "$_SERVER[DOCUMENT_ROOT]$path$fname.$e")) {
			switch($t) {
				case 1: // image
					if ($temp = @getimagesize($fn))
						return array('x' => $temp[0], 'y' => $temp[1], 'fn' => "$path$fname.$e", 'type' => strtolower($e));
				case 2: // flv video
					if (SUPPORT_FLV) {
						if (D_CACHE) {
							$ver = filemtime($fn);
							if (($c = cache($fn, $ver)) && $c['x'] && $c['y'])
								return array('x' => $c['x']['value'], 'y' => $c['y']['value'], 'fn' => "$path$fname.$e", 'type' => strtolower($e));
						}
						
						$getID3 = new getID3;
						$temp = $getID3->analyze($fn);
						if ($t1 = $temp['video']) {
							if (D_CACHE) setCache($fn, $ver, array('x' => $t1['resolution_x'], 'y' => $t1['resolution_y']));
							return array('x' => $t1['resolution_x'], 'y' => $t1['resolution_y'], 'fn' => "$path$fname.$e", 'type' => strtolower($e));
						}
					}
			}
		}

	return false;
}

// Fits x1/y1 dimensions to x2/y2 placeholder
// returns x1/y1 if both x2/y2 are 0
// fits to height or width if one of x2/y2 is 0
// if both x2 and y2 are defined:
// $mode: MI_FIT - fits to both height and width being proportionnally scaled
// $mode: MI_CUT CENTER - extract as much as possible from center to completely fit the desired size
function fitSize($x1, $y1, $x2, $y2, $mode = MI_FIT) {
	switch($mode) {

		case MI_FIT:

	if (!($x2 && $y2)) {	// if size is undefined
	
		if (!($x2 || $y2)) {	// both are undefined - take from image
			$x = $x1;
			$y = $y1;
		} else {
		
			if ($x2)	// width is fixed
				if ($x1 > $x2)
				$y = intval($y1 * ($x = $x2) / $x1);
				else { $x = $x1; $y = $y1; }
			else		// height is fixed
				if ($y1 > $y2)
				$x = intval($x1 * ($y = $y2) / $y1);
				else { $x = $x1; $y = $y1; }
		
		}
	} else {	// fit to fully defined size
		$x = $x1; $y = $y1;

		if ($x > $x2)	$y = intval($y1 * ($x = $x2) / $x1);
		if ($y > $y2)	$x = intval($x1 * ($y = $y2) / $y1);
	}

			return array($x, $y, 0, 0);
		
		case MI_CUT_CENTER:
		
			if (!($x2 && $y2)) {	// if size is undefined
			
				if (!($x2 || $y2)) {	// both are undefined - take from image
					$x = $x1;
					$y = $y1;
				} else {
				
					if ($x2)	// width is fixed
						if ($x1 > $x2)
						$y = intval($y1 * ($x = $x2) / $x1);
						else { $x = $x1; $y = $y1; }
					else		// height is fixed
						if ($y1 > $y2)
						$x = intval($x1 * ($y = $y2) / $y1);
						else { $x = $x1; $y = $y1; }
				
				}
			} else {	// fit to fully defined size
				$x = $x2; $y = $y2;

				if ($x != $x1)	$y = ($y2 * ($x = $x1) / $x2);
				if ($y > $y1)	$x = ($x2 * ($y = $y1) / $y2);

				$shiftX = intval(($x1 - $x) / 2);
				$shiftY = intval(($y1 - $y) / 2);
			}
			
			return array($x, $y, $shiftX, $shiftY);
	}
}


function fastimagecopyresampled (&$dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h, $quality = 3) {
  // Plug-and-Play fastimagecopyresampled function replaces much slower imagecopyresampled.
  // Just include this function and change all "imagecopyresampled" references to "fastimagecopyresampled".
  // Typically from 30 to 60 times faster when reducing high resolution images down to thumbnail size using the default quality setting.
  // Author: Tim Eckel - Date: 09/07/07 - Version: 1.1 - Project: FreeRingers.net - Freely distributable - These comments must remain.
  //
  // Optional "quality" parameter (defaults is 3). Fractional values are allowed, for example 1.5. Must be greater than zero.
  // Between 0 and 1 = Fast, but mosaic results, closer to 0 increases the mosaic effect.
  // 1 = Up to 350 times faster. Poor results, looks very similar to imagecopyresized.
  // 2 = Up to 95 times faster.  Images appear a little sharp, some prefer this over a quality of 3.
  // 3 = Up to 60 times faster.  Will give high quality smooth results very close to imagecopyresampled, just faster.
  // 4 = Up to 25 times faster.  Almost identical to imagecopyresampled for most images.
  // 5 = No speedup. Just uses imagecopyresampled, no advantage over imagecopyresampled.

  if (empty($src_image) || empty($dst_image) || $quality <= 0) { return false; }
  if ($quality < 5 && (($dst_w * $quality) < $src_w || ($dst_h * $quality) < $src_h)) {
    $temp = imagecreatetruecolor ($dst_w * $quality + 1, $dst_h * $quality + 1);
    imagecopyresized ($temp, $src_image, 0, 0, $src_x, $src_y, $dst_w * $quality + 1, $dst_h * $quality + 1, $src_w, $src_h);
    imagecopyresampled ($dst_image, $temp, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $dst_w * $quality, $dst_h * $quality);
    imagedestroy ($temp);
  } else imagecopyresampled ($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
  return true;
}

//	Creates JPEG from already loaded image $im of $x1/$y1 pixels size
//	New image resamples to $x2/$y2 and saves as JPEG to $fn
function makeImage($im, $x1, $y1, $x2, $y2, $fn, $mode = MI_FIT) {

	list($x, $y, $sx, $sy) = fitSize($x1, $y1, $x2, $y2, $mode);
	
	switch($mode) {
		case MI_FIT:
	$pim = imagecreatetruecolor ($x, $y);
			imagecopyresampled($pim, $im, 0, 0, 0, 0, $x, $y, $x1, $y1);
			break;
		case MI_CUT_CENTER:
			$pim = imagecreatetruecolor ($x2, $y2);
			imagecopyresampled($pim, $im, 0, 0, $sx, $sy, $x2, $y2, $x, $y);
			break;
	}
	
	touch($fn);
 	imagejpeg($pim, $fn, 80);
	imagedestroy($pim);
	
	return array('w' => $x, 'h' => $y, 'fn' => $fn, 'transparency' => false, 'type' => 'jpg');
}


//	Creates PNG with transparency from already loaded image $im of $x1/$y1 pixels size
//	New image resamples to $x2/$y2 and saves as PNG to $fn
// req: php 4.3.2+, gd 2.0.1+
function makeImagePNG($im, $x1, $y1, $x2, $y2, $fn, $mode = MI_FIT) {	

	list($x, $y, $sx, $sy) = fitSize($x1, $y1, $x2, $y2, $mode);

	switch($mode) {
		case MI_FIT:
	$pim = imagecreatetruecolor ($x, $y);
	imagealphablending($pim, false);
	imagesavealpha($pim,true);
			imagecopyresampled($pim, $im, 0, 0, 0, 0, $x, $y, $x1, $y1);
			break;
		case MI_CUT_CENTER:
			$pim = imagecreatetruecolor ($x2, $y2);
			imagealphablending($pim, false);
			imagesavealpha($pim,true);
			imagecopyresampled($pim, $im, 0, 0, $sx, $sy, $x2, $y2, $x, $y);
			$x = $x2;
			$y = $y2;
			break;
	}
	
	touch($fn);
	imagepng($pim, $fn);
	imagedestroy ($pim);
	
	return array('w' => $x, 'h' => $y, 'fn' => $fn, 'transparency' => true, 'type' => 'png');
}

//	Takes image from $_FILES[$field], resamples to $x/$y or leaves as is if zero $x or $y, saves to $path
function takeImage($field, $x0=0, $y0=0, $path='', $xtn=0, $ytn=0, $tnpath='', $imode = MI_FIT, $tnmode = MI_FIT) {
	global $MEDIA_TYPES;

	$o = array_extend(array(
		'field' => '', 
		'x0' => 0, 'y0' => 0, 'path' => '', 
		'xtn' => 0, 'ytn' => 0, 'tnpath' => '', 
		'imode' => MI_FIT, 'tnmode' => MI_FIT), 
		is_array($field) ? $field : array(
			'field' => $field, 
			'x0' => $x0, 'y0' => $y0, 'path' => $path, 
			'xtn' => $xtn, 'ytn' => $ytn, 'tnpath' => $tnpath, 
			'imode' => $imode, 'tnmode' => $tnmode));

	if ($f = (is_string($o['field'])) ? @$_FILES[$o['field']] : $o['field']) {

		if ($temp = @getimagesize($f['tmp_name'])) {	// regular image
			
			$fn = substr(strrchr($o['path'], '/'), 1);	// extract filename if any
			
			if (!$fn) { // take original filename if none provided
				$fn = $f['name'];

				if (($p = strrpos($fn, '.')) !== false)
					$fn = substr($fn, 0, $p);	// cut extension

				$o['path'] .= $fn;
				if ($o['tnpath'] && (substr($o['tnpath'], -1, 1) == '/'))
					$o['tnpath'] .= $fn;
					
			} else {
				if (($p = strrpos($o['path'], '.')) !== false)
					$o['path'] = substr($o['path'], 0, $p);	// cut extension
				if (($p = strrpos($o['tnpath'], '.')) !== false)
					$o['tnpath'] = substr($o['tnpath'], 0, $p);	// cut extension
			}
			
			list($x, $y) = fitSize($temp[0], $temp[1], $o['x0'], $o['y0'], $o['imode']);
			$imtype = $temp[2];
	
			if ($tnpath || !(($x == $temp[0]) && ($y == $temp[1]))) {
				switch ($imtype) {
					case 1:	$im = imagecreatefromgif ($f['tmp_name']);	break;
					case 2:	$im = imagecreatefromjpeg ($f['tmp_name']);	break;
					case 3:	$im = imagecreatefrompng ($f['tmp_name']);	break;
					
					case 4:		// swf
					case 13:	// swc
						move_uploaded_file($f['tmp_name'], $_SERVER['DOCUMENT_ROOT'].$o['path'].".{$MEDIA_TYPES[$imtype]}");
						return $size = array('w' => $temp[0], 'h' => $temp[1], 'fn' => $o['path'].".{$MEDIA_TYPES[$imtype]}", transparency => false);
						break;
						
					default:$im = 0;
				}
			}
			
			killMedia($o['path'], '');

//			if ($imtype && (($imtype == 1) || (($x == $temp[0]) && ($y == $temp[1])))) {	// got GIF or right sized image. GIF can be animated so don't process it.
			if ($imtype && (($temp[0] <= $x) && ($temp[1] <= $y))) {	// no animated GIFs uploaded for years of usage
				move_uploaded_file($f['tmp_name'], $_SERVER['DOCUMENT_ROOT'].$o['path'].".{$MEDIA_TYPES[$imtype]}");
				$size = array('w' => $x, 'h' => $y, 'fn' => $o['path'].".{$MEDIA_TYPES[$imtype]}", transparency => (($imtype == 1) || ($imtype == 3)), 'filename' => $fn.".{$MEDIA_TYPES[$imtype]}");
			} else
				if ($im)
					switch($imtype) {
						case 1:
						case 3:
							$size = makeImagePNG($im, $temp[0], $temp[1], $o['x0'], $o['y0'], $_SERVER['DOCUMENT_ROOT'].$o['path'].".png", $o['imode']);	// resample with transparency
							$size = array_merge($size, array('fn' => $o['path'].".png", 'filename' => $fn.".png"));
							break;
						case 2:
							$size = makeImage($im, $temp[0], $temp[1], $o['x0'], $o['y0'], $_SERVER['DOCUMENT_ROOT'].$o['path'].".jpg", $o['imode']);	//resample without transparency
							$size = array_merge($size, array('fn' => $o['path'].".jpg", 'filename' => $fn.".jpg"));
							break;
					}
				else 
					return false;
			
			if ($tnpath) {
	
					killImage($o['tnpath'], '');
	
					switch($imtype) {
						case 1:
						case 3:
								$tnsize = makeImagePNG($im, $temp[0], $temp[1], $o['xtn'], $o['ytn'], $_SERVER['DOCUMENT_ROOT'].$o['tnpath'].".png", $o['tnmode']);
								$size['tn'] = $o['tnpath'].".png";
							break;
						case 2:
								$tnsize = makeImage($im, $temp[0], $temp[1], $o['xtn'], $o['ytn'], $_SERVER['DOCUMENT_ROOT'].$o['tnpath'].".jpg", $o['tnmode']);
								$size['tn'] = $o['tnpath'].".jpg";
							break;
					}
			}
	
			if ($im) imagedestroy ($im);
			return $size;
			
		} else { // something other than image
			
			if (SUPPORT_FLV) {
				$getID3 = new getID3;
				$temp = $getID3->analyze($f['tmp_name']);
				
				if ($t = $temp['video']) {
					$x = $t['resolution_x'];
					$y = $t['resolution_y'];
					
					$imtype = 20;
					
					if ($old = isMedia($o['path'], ''))	// remove previous image
						@unlink($_SERVER['DOCUMENT_ROOT'].$o['path'].$old);
					
					if (!FLV_RESAMPLE_VIDEO || // don't resample video at all
						(($o['x0'] == $t['resolution_x']) && ($o['y0'] == $t['resolution_y']))) {	// got right sized media
						
						move_uploaded_file($f['tmp_name'], $_SERVER['DOCUMENT_ROOT'].$o['path'].".{$MEDIA_TYPES[$imtype]}");
						$size = array('w' => $x, 'h' => $y, 'fn' => $o['path'].".{$MEDIA_TYPES[$imtype]}", transparency => false, 'type' => $MEDIA_TYPES[$imtype], 'filename' => $fn.".{$MEDIA_TYPES[$imtype]}");
						return $size;
					}
					
				}
			}
		
		}
	}
	
	return false;
}


//	Takes image from $_FILES[$field], resamples to $x/$y or leaves as is if zero $x or $y, saves to $path
function takeJPG($field, $x0, $y0, $path, $xtn=0, $ytn=0, $tnpath='') {

	$f = (is_string($field)) ? @$_FILES[$field] : $field;

	if ( $f && ($temp = @getimagesize($f['tmp_name'])) ) {
	
		$fn = substr(strrchr($path, '/'), 1);	// extract filename if any
		
		if (!$fn) { // take original filename if none provided
			$fn = $f['name'];
			
			if (($p = strrpos($fn, '.')) !== false)
				$fn = substr($fn, 0, $p).'.jpg';	// cut old extension, add JPG one
			
			$path .= $fn;
			if ($tnpath && (substr($tnpath, -1, 1) == '/'))
				$tnpath .= $fn;
		}
		
		list($x, $y) = fitSize($temp[0], $temp[1], $x0, $y0);

		if ($tnpath || !(($temp[2] == 2) && ($x == $temp[0]) && ($y == $temp[1]))) {
			switch ($temp[2]) {
				case 1:	$im = imagecreatefromgif ($f['tmp_name']);	break;
				case 2:	$im = imagecreatefromjpeg ($f['tmp_name']);	break;
				case 3:	$im = imagecreatefrompng ($f['tmp_name']);	break;
				default:$im = 0;
			}
		}


		if (($temp[2] == 2) && ($x == $temp[0]) && ($y == $temp[1])) {	// got right sized JPG
			move_uploaded_file($f['tmp_name'], $_SERVER['DOCUMENT_ROOT'].$path);
			$size = array('w' => $x, 'h' => $y, 'fn' => $path);
		} else
			if ($im)
				$size = makeImage($im, $temp[0], $temp[1], $x0, $y0, $_SERVER['DOCUMENT_ROOT'].$path);
			else 
				return false;
		
		if ($tnpath) {
			$tnsize = makeImage($im, $temp[0], $temp[1], $xtn, $ytn, $_SERVER['DOCUMENT_ROOT'].$tnpath);
			$size['tn'] = $tnsize['fn'];
		}

		if ($im) imagedestroy ($im);
		return $size;
	}
	return false;
}

// *************** Static data access **********************

function sta() {
	global $STATIC;
	
	$args = func_get_args();
		foreach ($args as $k => $arg) $args[$k] = str_replace(array("'",'$'), array('',''), $arg);	// cleanup vars to disallow code execution
	
	$var = '$STATIC'."['".implode("']['",$args)."']";

	if (eval("return isset($var);")) 
		return eval("return $var;");
	else 
		return str_replace(array('_','-'), array(' ',' '), implode(' ',$args));
}

function dat() {
	global $DATA;
	
	$args = func_get_args();
		foreach ($args as $k => $arg) $args[$k] = str_replace(array("'",'$'), array('',''), $arg);	// cleanup vars to disallow code execution
	
	$var = '$DATA'."['".implode("']['",$args)."']";

	if (eval("return isset($var);")) 
		return eval("return $var;");
	else 
		return '';
//		return str_replace(array('_','-'), array(' ',' '), implode(' ',$args));
}

function h($s) {
	return validate_utf8($s) ? htmlspecialchars($s, ENT_QUOTES, 'UTF-8') : '';
}

function hdat() {
	global $DATA;
	
	$args = func_get_args();
		foreach ($args as $k => $arg) $args[$k] = str_replace(array("'",'$'), array('',''), $arg);	// cleanup vars to disallow code execution
	
	$var = '$DATA'."['".implode("']['",$args)."']";

	if (eval("return isset($var);")) 
		return is_string($t = eval("return $var;")) ? h($t) : $t;
	else 
		return '';
}


function def() {	// take defaults from session
	
	$args = func_get_args();
		foreach ($args as $k => $arg) $args[$k] = str_replace(array("'",'$'), array('',''), $arg);	// cleanup vars to disallow code execution
	
	$var = '$_SESSION[\'default\']'."['".implode("']['",$args)."']";

	if (eval("return isset($var);")) 
		return eval("return $var;");
	else 
		return false;
}

function setDef($name, $value) {	// take defaults from session
	return $_SESSION['default'][$name] = $value;	
}

function context($key, $def='') {
	global $CONTEXT;
	if (!isset($CONTEXT[$key])) return $def;
		else return $CONTEXT[$key];
}

function meta($key, $def='') { return isset($GLOBALS['META'][$key]) ? $GLOBALS['META'][$key] : $def; }

function config($key, $def='') { return isset($GLOBALS['DATA']['sitewide'][$key]) ? $GLOBALS['DATA']['sitewide'][$key] : $def; }

function xmeta($key, $method='best', $def='', $sep=',') {
	if (!$key) return '';
	switch($method) {
		case 'best':
			return ($t = meta($key, $def)) ? $t : config($key, $def);
			break;
		case 'all':
			return config($key, $def).$sep.meta($key, $def);
			break;
	}
	
	return false;
}

function hmeta($key, $def='') { return h(meta($key, $def)); }
function hxmeta($key, $def='') { return h(xmeta($key, $def)); }

function stack($key = '', $def = '') {
	if ($key) {
	if (is_string($key))
		return isset($GLOBALS['STACK'][$GLOBALS['SP']][$key]) ? $GLOBALS['STACK'][$GLOBALS['SP']][$key] : $def;
	else 
		if (is_array($key)) 
			$GLOBALS['STACK'][++$GLOBALS['SP']] = $key;
	} else
		return $GLOBALS['STACK'][$GLOBALS['SP']];
}
function unstack($data = false) { $GLOBALS['SP']--; return $data; }

function temp($key, $value) { return $GLOBALS['STACK'][$GLOBALS['SP']][$key] = $value; }

function action($keys) {	// return keyword from list which was found in $POST - i.e. detect action by form submit button's name
	foreach($keys as $key)
		if (isset($_POST[$key]))
			return $key;
	return false;
}

function code($hook, $list) {
	$temp = explode(',', $list);
	foreach ($temp as $t) {
		list($h,$c) = explode(':', $t);
		if (trim($h) == $hook) return trim($c);
	}
	return false;
}

function param($key, $def='') {
	global $CONTEXT;
	if (!isset($CONTEXT['params'][$key])) return $def;
		else return $CONTEXT['params'][$key];
}

// ***************** Data Access functions *****************

function arg($key, $def='') {
	global $CONTEXT;
	if (!isset($CONTEXT['args'][$key])) return $def;
		else return $CONTEXT['args'][$key];
}

function getdata($key, $def='') {
	if (!isset($_GET[$key])) return $def;
		else return $_GET[$key];
}

function postdata($key, $def='') {
	if (!isset($_POST[$key])) return $def;
		else return $_POST[$key];
}

function data($key, $def='') {
	if (!isset($_POST[$key])) 
		if (!isset($_GET[$key])) 
			return $def;
		else return $_GET[$key];
	else return $_POST[$key];
}

function tag() {
	if (!isset($_POST['tag'])) return 0;
		else return $_POST["tag"];
}

function block($b) {
	return '_block/_'.$b.'.php';
}

function tpl($b, $f = 'init', $params = array()) {
	$GLOBALS['STACK'][++$GLOBALS['SP']] = $params;
	$t = $f ? $b.'/'.$f : $b;
	
	return is_file($res = "$_SERVER[DOCUMENT_ROOT]/_template/$t.php") ? $res : false;
}

function bit($b, $f = '', $params = array()) {
	$GLOBALS['STACK'][++$GLOBALS['SP']] = $params;
	$t = $f ? $b.'/'.$f : $b;
	return "_bit/$t.php";
}

function bits($hook, $section = '') {
	$res = array();
	$t = $section ? $section.'/'.$hook : $hook;
	
	foreach((array)$GLOBALS['BITS'] as $v)
		if (is_file("_bit/$v/$t.php"))
			$res[] = "$v/$t";
			
	return sizeof($res) ? $res : false;
}




define('CLOB_SPACER', '<x>');
define('CLOB_EMPTY', '<e>');

function isClob($v) { return (($v == CLOB_SPACER) || ($v == CLOB_EMPTY)); }
function isClobEmpty($v) { return ($v == CLOB_EMPTY); }
function isClobFull($v) { return ($v == CLOB_SPACER); }


// prepare data from POST according to model using $record as template 
function prepareModel($model, $record) {
	global $DATA;
	
	$temp = array();
	
	foreach($model as $k => $v) {
		$t = postdata($k);
		
		if (is_array($v)) {
			if ($v['lang']) {
				foreach($DATA['langs'] as $lang) {
					$temp[] = array_merge($record, array(
						'name' => $k, 
						'value' => trim($t[$lang['text']]), 
						'assoc' => $lang['text'],
						'large' => $v['large']));
				}
			} else {
				$temp[] = array_merge($record, array(
					'name' => $k, 
					'value' => trim($t), 
					'large' => $v['large']));
			}
		} else {
			$temp[] = array_merge($record, array(
				'name' => $k,
				'value' => trim($t)));
		}
		
	}
	return $temp;
}

// creates flat array of the context information for component filtering
function flattenContext() {
	global $CONTEXT;
	if (isset($CONTEXT['flatten'])) return $CONTEXT['flatten'];

	$temp = array_merge($CONTEXT, $CONTEXT['params']);
	unset($temp['params']);
	
	$res = array();
	foreach($temp as $id => $v) {
		if (is_scalar($v))
			$res[$id] = $v;
		else 
		
		if (is_array($v))
			foreach($v as $vid => $vv)
				if (is_scalar($vv))
					$res[$id.'_'.$vid] = $vv;
	}
	return $CONTEXT['flatten'] = $res;
}

// this should return the part between the component path and the component template WITH all separators needed, i.e.
// /plugins/Articles/user  /show  / init.php
// -------- path -------- -route-   ---tpl-- 
// router should be found at $b/router.php
function component_route($b, $f = '', $params = array()) {
	$nm = $res = '';
	$bf = $f ? str_replace('//', '/', "$b/$f") : $b;

	if (is_array($params) && isset($params['name']) && ($nm = $params['name']))
		if (isset($GLOBALS['DATA']['components'][$params['name']]['route'][$bf]))
			return $GLOBALS['DATA']['components'][$params['name']]['route'][$bf];	// return previously calculated & cached route
	
	while (file_exists($_SERVER['DOCUMENT_ROOT'].FP_COMPONENT_PATH."$b{$res}/router.php")) {
		$temp = include $_SERVER['DOCUMENT_ROOT'].FP_COMPONENT_PATH."$b{$res}/router.php";

		if ($temp && is_string($temp))
			$res .= $temp;
		else
			break;
	}
	return $nm ? ($GLOBALS['DATA']['components'][$nm]['route'][$bf] = $res) : $res;	// cache if we can
}

// $ftag: array of tags to filter by
// $ctag: array keyed by tagnames (with filters), values are params
function component_filter($ftag, $ctag) {

//debug($ftag, 'ftag');
//debug($ctag, 'ctag');

	$anytag = in_array('*', $ftag);

	if ((!sizeof($ftag) || $anytag) && !sizeof($ctag)) return true;	//empty tag passes empty filter

	$reExtractFilter = "/\[(.*)\]/U";
	$reParseFilter = "/(?P<unary>[!])?(?P<key>[\w\d_]+)(?:(?P<binary>-|=)(?P<value>.*))?/";

	$context = flattenContext();
	
	foreach($ctag as $ct => $cparam) {
	
		if (preg_match_all($reExtractFilter, $ct, $filters, PREG_SET_ORDER)) { // some filters defined, check'em

			$t = ($p = strpos($ct, '[')) ? substr($ct, 0, $p) : COM_DEF_TAGNAME;
//debug($t, 'tag');
			$tag_matched = $anytag // directly specified '*' means "any tag"
				|| in_array($t, $ftag) // tag found in the matchlist
				|| (!sizeof($ftag) && ($t == COM_DEF_TAGNAME));	// empty filter and (empty tag or no tag at all)
//debug($tag_matched ? 't' : 'f', 'tag matched');

			if (!$tag_matched)
				continue;	// don't check filters if tag doesn't match

			
			

			foreach($filters as $f) {
				if (preg_match($reParseFilter, $f[1], $filter)) { // filter is correct

					switch((string)$filter['binary']) {
						case '-': 	// equal
						case '=':
							$res = (bool)($context[$filter['key']] == $filter['value']);
							break;
						
						case '': 	// empty binary means "exists"
						default:
							$res = isset($context[$filter['key']]);
							break;
					}

					switch($filter['unary']) {
						case '!': 	// negative
							$res = !$res;
					}
					
					if (!$res) continue 2;	// filter failed => tag failed too => skip to the next tag
					
				}	//	skip wrong filters
			}
			
			return true; // tag passed, no need to check others
			
		} else {	// no filters defined
		
			if ($anytag // directly specified '*' means "any tag"
				|| in_array($ct, $ftag) // tag found in the matchlist
				|| (!sizeof($ftag) && ($ct == COM_DEF_TAGNAME)))	// empty filter and (empty tag or no tag at all)
				return true;	// tag matches filter
		}
	}
	
	return false;	// no tags matched
}


function components($options = array()) {

	$protected = '@#$';
	
	$o = array('mask' => COM_ACTIVE, 'tag' => '');

	if (is_array($options))	$o = array_extend($o, $options);	else 
		if (is_int($options)) $o['mask'] = $options; else
			if (is_string($options)) $o['tag'] = trim($options);
	
	$o['mask'] = (int)$o['mask'];
	$o['tag'] = $o['tag'] ? explode(' ', $o['tag']) : array();

	$res = array();
	foreach ((array)$GLOBALS['DATA']['components'] as $k => $v) {

		if (($v['attr'] & $o['mask']) != $o['mask']) continue;	// strict filter by mask
		
		if (component_filter($o['tag'], $v['tags'])) $res[] = $k;	// conditional filter by tag
	}

//debug($options, "o");
//debug($res, "components");

	return $res;
}

function component_config($b, $f = '', $params = array()) {
	if ($f) {	// calculate route if the component path is separated from the template
		$route = component_route($b, $f, $params);
		$t = $b.$route.'/'.$f;
	} else	 	// use just what we get instead
		$t = $b;
	
	if (is_file($t = $_SERVER['DOCUMENT_ROOT'].FP_COMPONENT_PATH."$t.php")) {
		$GLOBALS['STACK'][++$GLOBALS['SP']] = $params;
		return $t;
	} else
		return false;
}

function component_exists($b) { return isset($GLOBALS['DATA']['components'][$b]); }

function component_localize($b) {
	global $DATA;
	stack(array('name' => $b));
	
	if (isset($DATA['components'][$b]))
		if ($type = $DATA['components'][$b]['value']) {
		
			$route = component_route("$type/user", '', array('name' => $b));
			if (is_file($t = $_SERVER['DOCUMENT_ROOT'].FP_COMPONENT_PATH."$type/user$route/lang-".context('lang').".php"))
				return $t;
		}
				
	return false;
}

function component($b, $params = array()) {	// if $params is string it interprets as array('tag' => $params)
	global $DATA;
	
	if (isset($DATA['components'][$b]))
		if ($type = $DATA['components'][$b]['value']) {
			$route = component_route("$type/user", "show", array('name' => $b));
			if (is_file($t = $_SERVER['DOCUMENT_ROOT'].FP_COMPONENT_PATH."$type/user$route/show.php")) {

				if (sizeof($params)) {
					$params = is_string($params) ? array('tag' => $params) : (is_array($params) ? $params : array());
					
					if (($tag = $params['tag']) && isset($DATA['components'][$b]['tags'][$tag]))
						$params = array_merge($params, array('data' => $DATA['components'][$b]['tags'][$tag]));
				}

				stack(array('name' => $b, 'params' => $params));
				return $t;
		}
	}
	
	stack(array('name' => $b, 'params' => $params));
	return $_SERVER['DOCUMENT_ROOT'].FP_COMPONENT_STUB;
}

function component_something($b, $what, $s='', $default = false) {
	global $DATA;
	if (isset($DATA['components'][$b]))
		if ($s) {
			if (isset($DATA['components'][$b][$what][$s]))
				return $DATA['components'][$b][$what][$s];
		} else
			return $DATA['components'][$b][$what];
			
	return $default;
}

function component_data($b, $s='') {
	return component_something($b, 'data', $s, $s ? false : array());
}

function component_settings($b, $s='') {
	return component_something($b, 'settings', $s);
}
			
function component_tags($b, $s='') {
	return component_something($b, 'tags', $s, array());
}

function component_fetch() {
	global $DATA;
	
	$_id = $DATA['page']['id'];
	$_lang = context('lang');
	
	
	if ($t = mc("tpl.mb.cpn_fetch.$_id.$_lang")) {
		return $DATA['components'] = $t;
	} else {		
	
		$DATA['components'] = array();
	
	if (sizeof($temp = datafetch_array('db_list', array('table' => tb('data'), 'where' => "id_data=".D_MULTIBLOCK." AND (id_parent={$DATA[page][id]} or id_parent=0) AND ".dbAndL('attr', COM_ENABLED), 'order' => 'id_index', 'index' => 'id' )))) {
	
		foreach ($temp as $k => $v) {
			$temp[$k]['settings'] = array();
			$temp[$k]['data'] = array();
			$temp[$k]['tags'] = array();
			
			if ($v['text']) {
				foreach(explode(',', $v['text']) as $tag) {
					$tx = explode(':', trim($tag), 2);
					$tagname = ($t = trim($tx[0])) ? $t : COM_DEF_TAGNAME;	// where to store tag data if no tag name exists
					$temp[$k]['tags'][$tagname] = (sizeof($tx) == 2) ? trim($tx[1]) : false;
				}
			}
			}
				

			$t = datafetch_array('loadmeta', array(D_MB_SETTINGS, D_MB_DATA), array(
				'parent' => array_keys($temp), 
				'assoc' => context('lang'),
				'getclob' => true,
				'source' => true));

		
		foreach ($t as $k => $v)
			switch ($v['id_data']) {
				case D_MB_SETTINGS:
					$temp[$v['id_parent']]['settings'][$v['name']] = $v['value'];
					break;
				case D_MB_DATA:
					$temp[$v['id_parent']]['data'][$v['name']] = $v['value'];
					break;
			}
	
			foreach ($temp as $k => $v) {
				unset($DATA['components'][$v['name']]);
			$DATA['components'][$v['name']] = $v;
			}
			
		}

		mcSet("tpl.mb.cpn_fetch.$_id.$_lang", $DATA['components'], MC_EXPIRE - mt_rand(2*60, 7*60));
	}

	return $DATA['components'];
}

function component_extract($name, $page = -1, $params = array()) {
	global $DATA;

	if (!$name) return false;
	
	$page = ($page == -1) ? ($a = context('isAdminPage') ? (int)param('node') : (int)$DATA['page']['id']) : (int)$page;

	if ($temp = $a ? datafetch('db_item', tb('data'), "id_data=".D_MULTIBLOCK." AND (id_parent=$page or id_parent=0) AND ".dbAndL('attr', COM_ENABLED)." and name='$name'", '', 'id_index DESC') : $DATA['components'][$name]) {
		if ($type = $temp['value']) {
			if (is_file($route = component_root($type).'data.php')) {
				stack(array('name' => $name, 'id' => $temp['id'], 'lang' => context('lang'), 'params' => $params));
				return include $route;
			}
		}
	}
}

function component_root($type, $level = false) {
	if (!$level) $level = 'user';
	return $_SERVER['DOCUMENT_ROOT'].FP_COMPONENT_PATH."$type/$level/";
}


/*** data fetch functions ***/

function dataset($dataset) {		// создается объект класса $dataset, в его метод init передаются остальные параметры, возвращается, если ОК

	if (include_once E.'_data/_'.$dataset.'.php') {

		$temp = strtoupper($dataset);
		$temp = new $temp;

		$args = func_get_args();
		$args = array_slice($args, 1);

		if (call_user_func_array( array(&$temp, 'init'), $args ))
			return $temp;
		else 
			return 0;	// инициализировать не получилось

	}
	 else return 0;
}

function datafetch($dataset) {		// создается объект класса $dataset, в его метод init передаются остальные параметры, возвращается, если ОК

	if (include_once E.'_data/_'.$dataset.'.php') {

		$temp = strtoupper($dataset);
		$temp = new $temp;

		$args = func_get_args();
		$args = array_slice($args, 1);

		if (call_user_func_array( array(&$temp, 'init'), $args ))
			return $temp->data;
		else 
			return NULL;	// инициализировать не получилось

	}
	 else return NULL;
}

function datafetch_array($dataset) {		// создается объект класса $dataset, в его метод init передаются остальные параметры, возвращается, если ОК

	if (include_once E.'_data/_'.$dataset.'.php') {

		$temp = strtoupper($dataset);
		$temp = new $temp;

		$args = func_get_args();
		$args = array_slice($args, 1);

		if (call_user_func_array( array(&$temp, 'init'), $args ))
			return $temp->data;
		else 
			return array();	// инициализировать не получилось

	}
	 else return array();
}

// **************** Array functions ***********************

function array_trim_empty_str($a) {	// kills empty string values from array, renumber keys
	$temp = array();
	
	foreach ($a as $v)
		if (($t = trim($v)) !== '')
			$temp[] = $t;
			
	return $temp;
}

function array_trim_empty($a) {	// kills empty values from array, don't renumber keys
	$temp = array();
	
	foreach ($a as $k => $v)
		if ($v) $temp[$k] = $v;
			
	return $temp;
}

function array_append(&$array, $data) {
	if (is_array($array) && $data) {
		if (is_array($data)) {
			foreach ($data as $d)
				$array[] = $d;
		} else
			$array[] = $data;
	}
}

function array_mark_bounds($array) {	// mark first and last array items with 'first' and 'last' elements
	if (is_array($array) && sizeof($array)) {
		
		foreach ($array as $k => $v) unset($array[$k]['first'], $array[$k]['last']);
		
		$k = array_keys($array);
		if (is_array($array[$temp = current($k)]))
			$array[$temp]['first'] = 'first';
		if (is_array($array[$temp = end($k)]))
			$array[$temp]['last'] = 'last';
	}
	return $array;
}

function array_extend($a1, $a2) {
	$res = array();
	foreach($a1 as $k => $v)
			$res[$k] = isset($a2[$k]) ? $a2[$k] : $v;
	return $res;
}

// ****************** SQL safe functions *******************

function sql_safe($s) {
	if (!get_magic_quotes_gpc())
		return addslashes($s);
	else
		return $s;
}

function sql_unsafe($s) {
	return $s;
}

function quot_safe($s) {
	return sql_safe($s);
}

function search_safe($s) {
	$s = preg_replace("/[^A-Za-zА-Яа-яЄІЇҐєіїґ0-9 _.,*&-]/u", ' ', strip_tags($s));
	return defined('USE_ADODB_OCI') ? str_replace("'", "''", $s) : addslashes($s);
}

function search_unsafe($s) {
	return defined('USE_ADODB_OCI') ? str_replace("''", "'", $s) : stripslashes($s);
}


// *********************** string functions ***************


function str_cut_long($string, $length, $break = '') {
	if (strlen($string) > $length) {
		if (($pos = strpos($string, " ", $length)) !== false)
			return substr($string, 0, $pos) . $break;
		else
			return substr($string, 0, $length) . $break;
	}
	return $string;
}

function str_safe($s) {
	return str_replace(array("'", '"', '%'), array("&ap;","&quot;","&perc;"), strip_tags(trim($s)));
//	return str_replace(array("'", '"', '%'), array("&ap;","&quot;","&perc;"), strip_tags(trim($s)));
}

function str_unsafe($s) {
	return str_replace(array("&ap;","&quot;","&perc;"), array("'", '"', '%'), $s);
}

function unh($s) {
//	return get_magic_quotes_gpc() ? $s : stripslashes($s);
	return stripslashes($s);
}

function validate_utf8($s) {
  return strlen($s) ? (preg_match('/^./us', $s) == 1) : true;
}

function html2text($s) {
	return preg_replace('/\s+/u', ' ', html_entity_decode(
		str_ireplace(array('&nbsp;', chr(194).chr(160), '—', '–', '&mdash;', '&ndash;', '’', '&rsquo;'), array(' ', ' ', '-', '-', '-', '-', "'", "'"), strip_tags($s)), ENT_COMPAT | ENT_HTML5, 'UTF-8'));
}

function text2search($s) {
	return str_replace(array('-', "'"), array('\-', "''"), $s);
}

// *************** HTTP Processing *********************

function postRequest($url, $referer, $_data) {

	// convert variables array to string:
	$data = array();
	if (is_array($_data) && sizeof($_data))
		foreach ($_data as $k => $v)
			$data[] = "$k=".urlencode($v);
	$data = implode('&', $data);
	// format --> test1=a&test2=b etc.
	
	// parse the given URL
	$url = parse_url($url);
	if ($url['scheme'] != 'http') return false;
	
	// extract host and path:
	$host = $url['host'];
	$path = $url['path'];
	$query = $url['query'];
	
	// open a socket connection on port 80
	$fp = fsockopen($host, 80);

	// send the request headers:
	fputs($fp, "POST $path?$query HTTP/1.1\r\n");
	fputs($fp, "Host: $host\r\n");
	fputs($fp, "Referer: $referer\r\n");
	fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
	fputs($fp, "Content-length: ". strlen($data) ."\r\n");
	fputs($fp, "Connection: close\r\n\r\n");
	fputs($fp, $data);
	
	$result = '';
	$counter = 1000;
	while(!feof($fp) && --$counter) {
	// receive the results of the request
		$result .= fgets($fp, 128);
	}
	
	// close the socket connection:
	fclose($fp);
	
	// split the result header from the content
	$result = explode("\r\n\r\n", $result, 2);
	
	$header = isset($result[0]) ? $result[0] : '';
	$content = isset($result[1]) ? $result[1] : '';
	
	// return as array:
	return array($header, $content);
}

// *************** HTML Processing *********************

function cp12512utf8($str) {
    $conv=array();
    for($x=128;$x<=143;$x++) $conv[$x+112]=chr(209).chr($x);
    for($x=144;$x<=191;$x++) $conv[$x+48]=chr(208).chr($x);
    $conv[184]=chr(209).chr(145); #ё
    $conv[168]=chr(208).chr(129); #Ё
    $conv[179]=chr(209).chr(150); #і
    $conv[178]=chr(208).chr(134); #І
    $conv[191]=chr(209).chr(151); #ї
    $conv[175]=chr(208).chr(135); #ї
    $conv[186]=chr(209).chr(148); #є
    $conv[170]=chr(208).chr(132); #Є
    $conv[180]=chr(210).chr(145); #ґ
    $conv[165]=chr(210).chr(144); #Ґ
    $conv[184]=chr(209).chr(145); #Ґ
    $ar=str_split($str);
    foreach($ar as $b) if(isset($conv[ord($b)])) $nstr.=$conv[ord($b)]; else $nstr.=$b;
    return $nstr;
}

function utf82cp1251($s)
{
  $r='';
  $state=1;
  for ($i=0;$i<strlen($s);$i++)
  {
    $c=ord($s[$i]);
    switch($state)
    {
      case 1: //not a special symbol
        if($c<=127)
        {
          $r.=$s[$i];
        }
        else
        {
          if(($c>>5)==6)
          {
            $c1=$c;
            $state=2;
          }
          else
            $r.=chr(128);
        }
        break;
      case 2: //an utf-8 encoded symbol has been meet
        $new_c2=($c1&3)*64+($c&63);
        $new_c1=($c1>>2)&5;
        $new_i=$new_c1*256+$new_c2;
        switch($new_i)
        {
          case 0x0401: $out_c=chr(168); break;	// Ё
          case 0x0451: $out_c=chr(184); break;	// ё
          case 0x0404: $out_c=chr(170); break;	// Є
          case 0x0454: $out_c=chr(186); break;	// є
          case 0x0406: $out_c=chr(178); break;	// І
          case 0x0456: $out_c=chr(179); break;	// і
          case 0x0407: $out_c=chr(175); break;	// Ї
          case 0x0457: $out_c=chr(191); break;	// ї
          case 0x0490: $out_c=chr(165); break;	// Ґ
          case 0x0491: $out_c=chr(180); break;	// ґ
          case 0x00ab: $out_c=chr(171); break;	// «
          case 0x00bb: $out_c=chr(187); break;	// »
          default: $out_c=chr($new_i-848);
        }
        $r.=$out_c;
        $state=1;
        break;
    }
  }
  return $r;
}



/*** attach youtube video ***/

define('RE_YOUTUBE_DETECT', '#<a.*\shref=[\"\'](.*(youtube\.com|youtu\.be).*)[\"\'].*\>.*\</a\>#iuU');
define('RE_YOUTUBE_ID_DETECT', "#(?<=v=)[a-zA-Z0-9-]+(?=&)|(?<=v\/)[^&\n]+(?=\?)|(?<=embed/)[^&\n]+|(?<=v=)[^&\n]+|(?<=youtu.be/)[^&\n]+#");

function attachYoutube($html) { return preg_replace_callback(RE_YOUTUBE_DETECT, 'attachYoutubeCallback', $html); }

function attachYoutubeCallback($matches) {
	
	if (preg_match(RE_YOUTUBE_ID_DETECT, $matches[1], $t)) {
		$w = defined('IS_VIDEO_X') ? IS_VIDEO_X : 640;
		$h = defined('IS_VIDEO_Y') ? IS_VIDEO_Y : 464;
		ob_start();
		?><iframe width="<?=$w?>" height="<?=$h?>" src="http://www.youtube.com/embed/<?=$t[0]?>" frameborder="0" allowfullscreen></iframe><?
		return ob_get_clean();
	}
		else return $matches[0];
}



/*** attach local video ***/

define('RE_MEDIA_DETECT', '/\<a href=\"((.*)\.flv)\".*\>(.*)\<\/a\>/iuU');

$_flash_objects = 0;

function attachVideo($html) { return preg_replace_callback(RE_MEDIA_DETECT, 'attachFlashCallback', $html); }

function attachFlashCallback($matches) {	// flowplayer 3.x required
	$oName = "sbflpa".++$GLOBALS['_flash_objects'];

	if (($f = checkImage($matches[1])) && ($w = $f['x']) && ($h = $f['y'])) {

		startScript('swfobject'); 
			?><script>swfobject.embedSWF("/lib/flowplayer.swf", "<?=$oName?>", "<?=$w?>", "<?=$h?>", "9.0.0", null, {config: "{'clip': { 'url': '<?=$f['fn']?>', 'autoPlay': false, 'autoBuffering': true }}"}, {wmode:"transparent"});</script><?
		endScript();

		ob_start();
			?><div id="<?=$oName?>"></div><?
		return ob_get_clean();

	} else return $matches[0];
}



/*** attach image ***/

define('RE_IMAGE_DETECT', '/\<img (.*)src=\"(.*)\.(jpg|png|gif)\"(.*)\>/iuU');	// smth, url, ext, smth

function attachImage($html) { return preg_replace_callback(RE_IMAGE_DETECT, 'attachImageCallback', $html); }

function attachImageCallback($matches) {
	$res = $matches[0];
	
	$title = preg_match('/title=\"(.*)\"/iuU', $matches[1].$matches[4], $t) ? $t[1] : t('Большая картинка в новом окне');
	
	$src = explode('/', $matches[2].'.'.$matches[3]);
	$fn = array_pop($src);

	$fs = array_shift($src);
	
	if ($fs == 'http:') {	// file is somewhere on the web
		unset($src[0]);
		$fs .= '/';
	} else	// file is on the local fs
		$fs = $_SERVER['DOCUMENT_ROOT'];

	if ($tn = array_pop($src)) {	// if an image is a thumbnail located in some dir
		if (is_file($fs.($im = '/'.implode('/',$src).'/'.$fn)) && ($size = @getimagesize($fs.$im))) {	// there is a full-size image in the upper dir
			$res = "<a href=\"#\" title=\"$title\" onclick=\"return showImage('$im', '$title', $size[0], $size[1]);\"><img $matches[1]src=\"$matches[2].$matches[3]\"$matches[4]></a>";
		}
	}

	return $res;
}
